package tech.yixiyun.framework.kuafu.controller.request.param;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.RandomUtil;
import tech.yixiyun.framework.kuafu.context.ApplicationContext;
import tech.yixiyun.framework.kuafu.controller.action.ActionContext;
import tech.yixiyun.framework.kuafu.kits.DateKit;
import tech.yixiyun.framework.kuafu.kits.StringKit;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.*;

public class ParamUtil {

    public static final char NESTED='.';
    public static final char BRACKET_START='[';
    public static final char BRACKET_END=']';
    public static final String EMPTY="";

    /**
     * 排除 . 或者 [ 的干扰
     * 如果是 aaa 返回 aaa
     * 如果是 aaa.b 或者 aaa[0]返回 aaa
     * 如果是 .b 或者 [b] 返回b
     * @param key
     * @return
     */
    public static String clean(String key) {
        if (StringKit.isBlank(key)) return key;
        int startIndex = -1;
        char c;
        char[] chars = key.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            c = chars[i];
            if (c != NESTED && c != BRACKET_START && c != BRACKET_END) {
                if (startIndex == -1) startIndex = i;
            }else{
                if (startIndex != -1) return key.substring(startIndex, i);
            }
        }
        if (startIndex > -1) return key.substring(startIndex);
        else return EMPTY;
    }



    /**
     * 将request传的数据组成对应参数对象
     * @param paramDefinition
     * @param paramGroupData
     * @return
     */
    public static Object compose(ParamDefinition paramDefinition, List<Map.Entry<String, String[]>> paramGroupData) {
        if (paramGroupData == null) {
            return ParamType.getDefaultValue(paramDefinition.getParameter().getType());
        } else {
            Object result = null;
            for (Map.Entry<String, String[]> item : paramGroupData) {
                result = processParamValue(result,
                        paramDefinition.getParamFeature(), item.getValue(),  item.getKey().substring(paramDefinition.getName().length()));
            }
            return result;
        }
    }


    /**
     * 转化请求参数
     * @param result
     * @param feature
     * @param paramValues
     * @param remainKey
     * @return
     */
    private static Object processParamValue(Object result, ParamDefinition.ParamFeature feature, String[] paramValues, String remainKey) {

        switch (feature.getParamType()) {
            case COMMON:
                return processCommonParamValue(feature, paramValues);
            case ARRAY:
                return processArrayParamValue(feature, paramValues, remainKey);
            case POJO:
                return processPojoParamValue(result, feature, paramValues, remainKey);
            case LIST:
                return processListParamValue(result, feature, paramValues, remainKey);
            case MAP:
                return processMapParamValue(result, feature, paramValues, remainKey);

        }


        return result;
    }


    /**
     *
     * @param feature
     * @param paramValues
     * @return
     */
    private static Object processCommonParamValue(ParamDefinition.ParamFeature feature, String[] paramValues) {
        //上传文件要特殊处理
        if (feature.getClazz() == UploadFile.class) {

            return convertToUploadFile(paramValues[0]);
        }
        return StringKit.convert(StringKit.join(",", paramValues ), feature.getClazz());
    }

    /**
     * 将临时文件转为UploadFile对象
     * @param tempPath
     * @return
     */
    private static UploadFile convertToUploadFile(String tempPath) {
        if (StringKit.isBlank(tempPath)) return null;
        File tempFile = new File(tempPath);
        if (tempFile.exists() == false) return null;
        tempPath = tempFile.getName();
        //临时文件地址，命名方式为 randomstr_uuid_randomstr_originalname
        int index = tempPath.indexOf("_");
        String randomStr = tempPath.substring(0, index);
        index = tempPath.indexOf("_"+randomStr+"_");
        String originalName =  tempPath.substring(index + randomStr.length() + 2);
        UploadFile file = new UploadFile();
        file.setOriginalName(originalName);
        String savePath = getFileSavePath(originalName);
        file.setRelativePath(savePath);
        file.setSize(tempFile.length());
        file.setSuffix(getFileSuffix(originalName));
        try {
            File newFile = new File(ApplicationContext.getServletContext().getRealPath(savePath));
            file.setAbsolutePath(newFile.toURI().getPath());
            FileUtils.moveFile(tempFile, newFile);
        } catch (IOException e) {
            throw new RuntimeException("上传文件保存到目标路径["+savePath+"]时发生错误，操作失败");
        }
        return file;
    }

    private static String getFileSuffix(String fileName) {
        int i = fileName.lastIndexOf(".");
        return i > -1 ? fileName.substring(i + 1) : "";
    }

    private static String getFileSavePath(String originalName) {
        String savePath = ActionContext.getAction().getRoute().getSavePath();
        if (StringKit.isBlank(savePath)) {
            throw new RuntimeException("获取上传文件的保存路径失败，上传失败");
        }
        String[] patterns = StringKit.matchAll(savePath, "(?<=\\{)[^}]+");
        for (String p : patterns) {
            String[] strs = p.split(":");
            switch (strs[0]) {
                case "uuid" :
                    savePath = StringKit.replace(savePath, "{"+p+"}",  UUID.fastUUID().toString(true));
                    break;
                case "date":
                    savePath = StringKit.replaceOnce(savePath, "{"+p+"}",  DateKit.format(new Date(), strs[1]));
                    break;
                case "suffix":
                    String suffix = getFileSuffix(originalName);
                    savePath = StringKit.replaceOnce(savePath, "{"+p+"}", "".equals(suffix) ?  "_null_" :suffix);
                    break;
                case "random":
                    savePath = StringKit.replaceOnce(savePath, "{"+p+"}", RandomUtil.randomString(Integer.parseInt(strs[1])));
                    break;
                case "session":
                    Object value = ActionContext.getRequest().getSession().getAttribute(strs[1]);
                    savePath = StringKit.replaceOnce(savePath, "{"+p+"}", value == null ? "_null_":value.toString());
                    break;
                case "originalName":
                    savePath = StringKit.replaceOnce(savePath, "{"+p+"}", originalName);
                    break;

            }
        }
        return savePath;
    }

    /**
     * 处理Map参数值
     * @param result
     * @param feature
     * @param paramValues
     * @param remainKey
     * @return
     */
    private static Object processMapParamValue(Object result, ParamDefinition.ParamFeature feature, String[] paramValues, String remainKey) {
        if (result == null) {
            if (feature.getClazz() == Map.class) {
                result = new HashMap<>();
            } else {
                try {
                    result = feature.getClazz().newInstance();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
        Map map = (Map) result;

        String key = clean(remainKey);
        Object keyObj = processParamValue(null, feature.getSubParamFeatures()[0], new String[]{key}, "");
        map.put(keyObj, processParamValue(map.get(keyObj), feature.getSubParamFeatures()[1], paramValues,
                remainKey.substring(key.length() + 1)));

        return map;

    }

    /**
     * 处理List字段值
     * @param result
     * @param feature
     * @param paramValues
     * @param remainKey
     * @return
     */
    private static Object processListParamValue(Object result, ParamDefinition.ParamFeature feature, String[] paramValues, String remainKey) {
        if (result == null) {
            if (feature.getClazz() == List.class) {
                result = new ArrayList<>();
            } else {
                try {
                    result = feature.getClazz().newInstance();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }

        List list = (List) result;

        String key = clean(remainKey); //[1].name 可以获取到1
        Integer index = Integer.parseInt(key);
        if (list.size() < index + 1) {
            for (int i = list.size(); i < index + 1; i++) {
                list.add(null);
            }
        }
        list.set(index, processParamValue(list.get(index), feature.getSubParamFeatures()[0], paramValues,
                remainKey.substring(key.length() + 2)));
        return list;
    }



    /**
     * 处理pojo字段值
     * @param result
     * @param feature
     * @param paramValue
     * @param remainKey
     * @return
     */
    private static Object processPojoParamValue(Object result, ParamDefinition.ParamFeature feature, String[] paramValue, String remainKey) {
        if (result == null) {
            try {
                result = feature.getClazz().newInstance();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        String key = clean(remainKey); //.user.name 可以获取到 user

        if (StringKit.isNotBlank(key)) {
            for (ParamDefinition.ParamFeature subParamFeature : feature.getSubParamFeatures()) {
                if (subParamFeature.getField().getName().equals(key)) {
                    try {
                        Object value = subParamFeature.getField().get(result);
                        subParamFeature.getField().set(result,
                                processParamValue(value, subParamFeature, paramValue, remainKey.substring(key.length() + 1)));
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                    break;
                }
            }
        }

        return result;


    }

    /**
     * 处理数组元素
     * @param paramValue
     * @param remainKey
     * @return
     */
    private static Object processArrayParamValue(ParamDefinition.ParamFeature feature, String[] paramValue, String remainKey) {
        if (paramValue == null) return null;
        Object array = Array.newInstance(feature.getSubParamFeatures()[0].getClazz(), paramValue.length);

        for (int i = 0; i < paramValue.length; i++) {
            Array.set(array, i, processParamValue(null, feature.getSubParamFeatures()[0], new String[]{paramValue[i]}, ""));

        }

        return array;

    }
}
